#pragma once
#include <vector>
#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>
#include <mutex>
#include <boost/algorithm/string.hpp>
#include "cppjieba/Jieba.hpp"
#include "log.hpp"
namespace ns_util
{
	class FileUtil
	{
		public:
			static bool ReadFile(const std::string& file_path,std::string* out)
			{
				//先把这个文件打开
				std::ifstream in(file_path,std::ios::in);
				if(!in.is_open())
				{
					std::cerr << "open file" << file_path << "error!" << std::endl;
					return false;
				}

				//用getline方法按行读取文件内容
				//问：如何理解getline返回值istream& 作为判断条件呢？
				//while(bool),本质是因为返回的流类型重载了强制类型转化，当读取到文件结尾时返回false
				std::string line;
				while(std::getline(in,line))
					*out += line;
				in.close();
				return true;
			}
	};


	class StringUtil
	{
	public:
		static void Split(const std::string& target,std::vector<std::string>* out,const std::string& sep)
		{
			//boost split函数介绍
			//第一个参数：切分后的字符串存放地址
			//第二个参数：数据源
			//第三个参数：分隔符列表，其中的每个字符都会作为切分的分隔符
			//第四个参数：可选为boost::token_compress_on 或 boost::token_compress_off，默认为后者，当为off时，会保留两个连续分隔符中间的空白，意味着每两个连续的分隔符就会往结果中存一个空白字符串；而为on时，这个空白不会被保留，两个连续的分隔符不会为结果留下任何内容。可以理解为：有多个连续的相邻分隔符时，压缩为一个分隔符。
			boost::split(*out,target,boost::is_any_of(sep),boost::token_compress_on);
		}
	};
	


	const char* const DICT_PATH = "./dict/jieba.dict.utf8";
	const char* const HMM_PATH = "./dict/hmm_model.utf8";
	const char* const USER_DICT_PATH = "./dict/user.dict.utf8";
	const char* const IDF_PATH = "./dict/idf.utf8";
	const char* const STOP_WORD_PATH = "./dict/stop_words.utf8";
	class JiebaUtil
	{
	//在实际的实践中，发现CutForSearch会将诸如空格、is这种在所有文档内大量存在的暂停词保留。当用户输入这些暂停词时，得到的结果就可能是所有文件。因此，要改动这部分代码，手动将暂停词去除。但是这个过程会非常漫长！
	public:
		void InitJiebaUtil()
		{
			std::ifstream in(STOP_WORD_PATH);
			if(!in.is_open())
			{
				LOG(FATAL,"load stop words file error!");
				return;
			}
			std::string line;
			while(std::getline(in,line))
			{
				stop_words.insert({line,true});
			}
			in.close();
		}

		void CutStringHelper(const std::string& src, std::vector<std::string>* out)
		{
			jieba.CutForSearch(src,*out);
			//此处out内还含有暂停词。为了更好的搜索体验，应该把暂停词去除！
			for(auto iter = out->begin();iter != out->end();)//这里不用iter++，因为会涉及迭代器失效问题，迭代器迭代会在循环逻辑里处理
			{
				auto it = stop_words.find(*iter);
				if(it != stop_words.end())
				{
					//走到这里说明当前的*it是暂停词，需要去掉
					iter = out->erase(iter);//erase自动指向下一个元素
				}
				else
					iter++;
			}
		}

		static void CutString(const std::string& src,std::vector<std::string>* out)
		{
			//jieba.CutForSearch(src,*out);
			//加载单例、初始化（仅第一次）、获得CutStringHelper、调用jieba分词去掉暂停词
			ns_util::JiebaUtil::get_instance()->CutStringHelper(src,out);
		}

		static JiebaUtil* get_instance()
		{
			static std::mutex mtx;
			if(nullptr == instance)
			{
				mtx.lock();
				if(nullptr == instance)
				{
					instance = new JiebaUtil();  
					instance->InitJiebaUtil();
				}
				mtx.unlock();
			}
			return instance;
		}
	private:
		//定义为static防止每次想分词都要建立JiebaUtil对象麻烦
		//static cppjieba::Jieba jieba;
		cppjieba::Jieba jieba;
		std::unordered_map<std::string,bool> stop_words;
		//构造私有、拷贝赋值delete保证单例，因为涉及文件操作，只需要加载一次
		JiebaUtil()
		:jieba(DICT_PATH,HMM_PATH,USER_DICT_PATH,IDF_PATH,STOP_WORD_PATH)
		{}
		JiebaUtil(const JiebaUtil&) = delete;
		JiebaUtil& operator=(const JiebaUtil&) = delete;

		static JiebaUtil* instance;
	};
	JiebaUtil* JiebaUtil::instance = nullptr;
	//cppjieba::Jieba	JiebaUtil::jieba(DICT_PATH,HMM_PATH,USER_DICT_PATH,IDF_PATH,STOP_WORD_PATH); 
}




















